package tr.gov.tubitak.uekae.applet.light;

import netscape.javascript.JSException;
import netscape.javascript.JSObject;
import org.apache.log4j.PropertyConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tr.gov.tubitak.uekae.applet.WebLoginApplet;
import tr.gov.tubitak.uekae.applet.resource.ResourceUtil;
import tr.gov.tubitak.uekae.esya.api.common.crypto.Algorithms;
import tr.gov.tubitak.uekae.esya.api.common.crypto.BaseSigner;
import tr.gov.tubitak.uekae.esya.api.common.util.LicenseUtil;
import tr.gov.tubitak.uekae.esya.api.smartcard.pkcs11.BaseSmartCard;
import tr.gov.tubitak.uekae.esya.api.smartcard.pkcs11.SmartCardException;

import java.applet.Applet;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.cert.X509Certificate;

/**
 * @author suleyman.uslu
 */
public class LightApplet extends Applet {

    private static Logger msLogger = LoggerFactory.getLogger(WebLoginApplet.class);
    LightSmartCardManager scManager;
    public String mLoginFormCerts;
    int mTerminalNo;
    String mTerminal;
    X509Certificate mCert;
    BaseSigner mBaseSigner;
    //String mLoginFormTerminals;

    @Override
    public void start() {
    }

    /**
     * Initialization function of the applet
     */
    public void init() {

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

        //public Object run() {

        parameterInit();
        loginInit();
        msLogger.debug("initialization successful :)");
        //return null;
        //}
        //});
    }



    /**
     * Initializes required parameters
     */
    private void parameterInit() {

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

        //public Object run() {

        try {
            PropertyConfigurator.configure(new FileInputStream(ResourceUtil.log4jPath));
            msLogger.debug("Log4j is used. Configuration path is " + ResourceUtil.log4jPath);

            LicenseUtil.setLicenseXml(new FileInputStream(ResourceUtil.licensePath));
            msLogger.debug("License is being loaded from " + ResourceUtil.licensePath);
        }
        catch (Exception e) {
            msLogger.debug(e.getMessage(), e);
        }

        //return null;
        //}
        //});
    }

    /**
     * Finds the certificates in smart cards and passes to
     * Javascript for certificates to be printed in the HTML form
     */
    private void loginInit()/* throws PrivilegedActionException */{

        final JSObject jsObject = JSObject.getWindow(this);

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

        //public Object run() {

        String loginFormCerts = "";

        try {

            //scManager = SmartCardManager.getInstance();
            scManager = LightSmartCardManager.getInstance();
            scManager.listCertificatesOfTerminals();

            //loginFormCerts = loginFormCerts.concat("<option value=\"NoCert\">Please Select Certificate</option>");
            loginFormCerts = loginFormCerts.concat("<table>");
            loginFormCerts = loginFormCerts.concat("<tr><th></th><th>Please your certificate</th></tr>");

            //for(ECertificate cert : scManager.signatureCerts) {
            for(X509Certificate cert : scManager.signatureCerts) {

                String certName = cert.getSubjectX500Principal().getName();
                String issuer = cert.getIssuerX500Principal().getName();
                BigInteger certSerialHex = cert.getSerialNumber();

                //loginFormCerts = loginFormCerts.concat("<option value=\""+certSerialHex+"\">"+certName+"</option>");
                loginFormCerts = loginFormCerts.concat("<tr><td><input type='radio' name='certRadios' value='"+certSerialHex+"' /></td><td>"+certName+"</td></tr>");
                loginFormCerts = loginFormCerts.concat("<tr><td></td><td>"+issuer+"</td></tr>");
            }
            loginFormCerts = loginFormCerts.concat("</table>");

        } catch (SmartCardException e) {
            e.printStackTrace();
        }

        //if(mLoginFormCerts == null || mLoginFormCerts == "")
        mLoginFormCerts = loginFormCerts;

        // call javascript function to print certificates of connected smart cards to login page
        jsObject.call("setLoginFormCerts", new Object[]{mLoginFormCerts});

        //return null;
        //}
        //});
    }

    /**
     * Called from home page and signs given document with signer that applet holds
     * @param doc    document to be signed
     */
    public void signDocument(String doc) {

        JSObject jsObject = JSObject.getWindow(this);

        msLogger.debug("Document is being signed in home page");

        //byte[] signature = null;

        /** PKCS7 **/
        PKCS7Ops pkcs7Ops = new PKCS7Ops(doc.getBytes(), mCert, mBaseSigner);
        //signature = pkcs7Ops.sign();

        msLogger.debug("Document is signed successfully");
        String signature64 = pkcs7Ops.sign();//Base64.encode(signature);

        // Prints signature in base64 format to HTML
        jsObject.call("setSignature64", new Object[]{signature64});
    }

    /**
     * Called when logout button is clicked and logs out from smart card
     * @throws SmartCardException
     */
    public void logout() throws SmartCardException {

        msLogger.debug("logout from applet is called");
        try {
            // logout from smart card
            BaseSmartCard bsc = scManager.smartCards.get(mTerminalNo);
            bsc.logout();
            msLogger.debug("logged out");
        }
        catch (Exception e) {
            msLogger.debug("error in logging out and closing session", e);
        }
    }

    /**
     * Signs the session id, called from login.jsp
     * @param doc document to be signed
     * @param certHex certificate serial no in hex form
     * @param pin pin of smart card
     */
    public void signFromHTML(final String doc, final String certHex, final String pin) {

        final JSObject jsObject = JSObject.getWindow(this);
        msLogger.debug("Signing process for login has started");                                                        //jsObject.call("check", new Object[]{"1a"});

        // jar imzali da olsa yetkilerde sorun cikiyordu AccessController olmadan
        //AccessController.doPrivileged(new PrivilegedAction() {

        //public Object run() {

        int certNo = -1;
        //for(ECertificate cert : scManager.signatureCerts) {
        for(X509Certificate cert : scManager.signatureCerts) {                                                          //jsObject.call("check", new Object[]{"2a"});

            certNo++;

            //if(certHex.equals(cert.getSerialNumberHex())) {
            if(certHex.equals(cert.getSerialNumber().toString())) {                                                     //jsObject.call("check", new Object[]{"3a"});

                try {
                    msLogger.debug("matching certificate found " + certHex + " " + cert.getSerialNumber().toString());  //jsObject.call("check", new Object[]{"4a"});
                    byte[] docBytes = doc.getBytes("UTF-8");

                    mCert = cert;
                    msLogger.debug("Certificate No : " + certNo);

                    String terminal = scManager.certTerminalMap.get(certNo);
                    mTerminal = terminal;
                    msLogger.debug("Terminal : " + terminal);

                    int terminalNo = scManager.mTerminalList.indexOf(terminal);
                    mTerminalNo = terminalNo;
                    msLogger.debug("Terminal No : " + terminalNo);

                    BaseSmartCard baseSmartCard = scManager.smartCards.get(terminalNo);

                    // session acmaktan vazgectim, sertifika listesini okurken actigim sessionlari
                    // acik birakiyorum, boylece yeniden session acmak icin zaman harcamiyorum
                    // kullanilmayacak kartlardaki sessionlari kapatiyorum, imza attiktan sonra
                    // bizim karttaki session'i da kapatiyorum, dezavantaji session acik kalabiliyor
                    // islem yapilmazsa, diger bir yontem ise session'lari acik birakmak

                    // imza icin kullanilacak terminal haric diger terminal session'larini kapat
                            /*for (String term : scManager.mTerminalList) {
                                if (!term.equals(terminal)) {
                                    int termNo = scManager.mTerminalList.indexOf(term);
                                    scManager.smartCards.get(termNo).closeSession();
                                }
                            }*/

                    msLogger.debug("Logging in to smart card");
                    try {
                        baseSmartCard.login(pin);
                    } catch (Exception e) {
                        msLogger.debug("Error in login to smart card", e);
                        e.printStackTrace();
                        msLogger.debug(e.getMessage());
                    }

                    BaseSigner baseSigner = baseSmartCard.getSigner(cert/*.asX509Certificate()*/, Algorithms.SIGNATURE_RSA_SHA256);

                    mBaseSigner = baseSigner;

                    //byte[] signatureBytes = null;

                    /** sign PKCS7 signature **/
                    PKCS7Ops pkcs7Ops = new PKCS7Ops(docBytes, cert, baseSigner);
                    //signatureBytes = PKCS7Ops.signPKCS7Light();

                    //String signature = new String(signatureBytes);
                    //msLogger.debug("Signature" + signature);

                    //jsObject.call("check", new Object[]{"before sign"});
                    String signature64 = pkcs7Ops.sign();//Base64.encode(signatureBytes);
                    //jsObject.call("check", new Object[]{"after sign"});


                    // close terminal session
                            /*msLogger.debug("Closing session");
                            baseSmartCard.closeSession();
                            if(!baseSmartCard.isSessionActive())
                                msLogger.debug("Session is successfully closed");
                            else
                                msLogger.debug("Error in session closing");*/

                    // Print signature to HTML in base 64 format
                    //jsObject.call("check", new Object[]{"before set signature"});
                    jsObject.call("setSignature64", new Object[]{signature64});
                    //jsObject.call("check", new Object[]{"after set signature"});

                } catch (UnsupportedEncodingException e) {
                    msLogger.debug(e.getMessage(), e);
                } catch (JSException e) {
                    msLogger.debug(e.getMessage(), e);
                } catch (SmartCardException e) {
                    msLogger.debug(e.getMessage(), e);
                }
            }
        }

        //    return null;
        //}
        //});
    }

    public static void main(String[]args) {

    }

}
